package com.zzyl.job;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.constant.CacheConstants;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertDataMapper;
import com.zzyl.mapper.AlertRuleMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.mapper.UserRoleMapper;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.DeviceDataVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Component
public class AlertJob {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    DeviceMapper deviceMapper;

    @Autowired
    ScriptEngine scriptEngine;

    @Autowired
    UserRoleMapper userRoleMapper;


    //设备维护人员的角色名称
    @Value("${zzyl.alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;
    //超级管理员的角色名称
    @Value("${zzyl.alert.managerRole}")
    private String managerRole;


    private static final Logger log = LoggerFactory.getLogger(AlertJob.class);
    @Autowired
    AlertRuleMapper alertRuleMapper;

    @Scheduled(cron = "0/3 * * * * *")
    public void deviceDataAlertFilter(){
        Integer count = alertRuleMapper.getAllCount();
        if (count==0){
            log.error("还未设置报警规则");
            return;
        }
        List<Object> jsonStrList = stringRedisTemplate.boundHashOps(CacheConstants.DEVICE_LASTDATA_CACHE_KEY).values();
        if (ObjectUtil.isEmpty(jsonStrList)){
            log.error("设备未上传数据");
            return;
        }

        //3.设备上报数据不为空，提取设备上报数据为list
        List<DeviceDataVo> deviceDataVoList = new ArrayList<>();
        jsonStrList.forEach(json -> deviceDataVoList.addAll(JSONUtil.toList(json.toString(), DeviceDataVo.class)));
        for (DeviceDataVo deviceData:deviceDataVoList){
            alertFilter(deviceData);
        }
    }


    void alertFilter(DeviceDataVo deviceData){

        LocalDateTime alarmTime = deviceData.getAlarmTime();
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60) {
            log.error("数据时间超过1分钟，不进行报警");
            return;
        }

        AlertRule alertRule= new AlertRule();
        alertRule.setIotId(deviceData.getIotId());
        alertRule.setProductKey(deviceData.getProductKey());
        alertRule.setFunctionId(deviceData.getFunctionId());
        List<AlertRule> alertRuleList=alertRuleMapper.selectByDeviceData(alertRule);
        if (ObjectUtil.isEmpty(alertRuleList)){
            return;
        }



        for (AlertRule rule:alertRuleList){
            String silentCacheKey = CacheConstants.ALERT_SILENT_PREFIX + deviceData.getIotId() + ":" + deviceData.getFunctionId() + ":" + rule.getId();
            //第一层校验：判断是否在生效时段内
            String aggCountKey = CacheConstants.ALERT_TRIGGER_COUNT_PREFIX +
                    deviceData.getIotId() + ":" + deviceData.getFunctionId() + ":" + rule.getId();
            String[] aepArr = rule.getAlertEffectivePeriod().split("~");
            LocalTime startTime = LocalTime.parse(aepArr[0]);
            LocalTime endTime = LocalTime.parse(aepArr[1]);
            LocalTime dTime = LocalDateTimeUtil.of(deviceData.getAlarmTime()).toLocalTime();
            if (startTime.isAfter(dTime)||endTime.isBefore(dTime)){
                log.error("不在查询时间内");
                return;
            }
            String str =  deviceData.getDataValue()+rule.getOperator()+rule.getValue();
            try {
                 Boolean eval = (Boolean) scriptEngine.eval(str);
                if (eval){
                    String silentData = stringRedisTemplate.boundValueOps(silentCacheKey).get();
                    if (ObjectUtil.isNotEmpty(silentData)){
                        //沉默周期内，不进行报警
                        log.error("沉默周期内，不进行报警");
                        return;
                    }
                    String countStr = stringRedisTemplate.boundValueOps(aggCountKey).get();
                    Integer count;
                    if (ObjectUtil.isEmpty(countStr)) {
                        count = 1;
                    } else {
                        assert countStr != null;
                        count = Integer.parseInt(countStr)+1;
                    }
                    if (count<3) {
                        stringRedisTemplate.boundValueOps(aggCountKey).set(count.toString());
                        log.error("报警次数未达到3次，不进行报警");
                        return;
                    }
                        stringRedisTemplate.delete(aggCountKey);
                        stringRedisTemplate.boundValueOps(silentCacheKey).set(silentCacheKey,rule.getAlertSilentPeriod(), TimeUnit.MINUTES);
                        //TODO 发送报警
                        setAlarmData(rule, deviceData);
                }else {
                    log.error("未达到阈值，不需要进行报警");
                    stringRedisTemplate.delete(aggCountKey);
                    continue;
                }
            } catch (ScriptException e) {
                log.error("报警规则表达式错误");
                return;
            }

        }
    }
    void setAlarmData( AlertRule alertRule, DeviceDataVo deviceDataVo){
        //获取消息的消费者
        List<Long> consumerIds = null;
        if (ObjectUtil.equals(0, alertRule.getAlertDataType())) {
            //如果是老人报警数据，需要通知绑定老人的护理员。根据iotId查询老人绑定的护理员
            if(deviceDataVo.getLocationType() == 0){
                consumerIds = deviceMapper.selectNursingIdsByIotIdWithElder(deviceDataVo.getIotId());
            }else if(deviceDataVo.getLocationType() == 1 && deviceDataVo.getPhysicalLocationType() == 2){
                consumerIds = deviceMapper.selectNursingIdsByIotIdWithBed(deviceDataVo.getIotId());
            }
        } else {
            //如果是设备报警数据，需要通知设备维护人员。根据指定角色名称查询相关用户
            consumerIds = userRoleMapper.selectUserIdsByRoleName(deviceMaintainerRole);
        }
        //查询超级管理员，超级管理员无论什么消息都会接收
        List<Long> managerIds = userRoleMapper.selectUserIdsByRoleName(managerRole);
        List<Long> allConsumerIds = CollUtil.addAllIfNotContains(consumerIds, managerIds);
        allConsumerIds = CollUtil.distinct(allConsumerIds);

        //新增报警数据
        insertAlertData(deviceDataVo, alertRule, allConsumerIds);
    }


    @Autowired
    private AlertDataMapper alertDataMapper;

    /**
     * 新增报警数据
     *
     * @param deviceDataVo 设备上报数据
     * @param alertRule    报警规则
     * @param consumerIds  消费者id列表
     * @return 报警数据
     */
    private void insertAlertData(DeviceDataVo deviceDataVo, AlertRule alertRule, List<Long> consumerIds) {
        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRule.getFunctionName(), alertRule.getOperator(), alertRule.getValue(), alertRule.getDuration());
        AlertData alertData = BeanUtil.toBean(deviceDataVo, AlertData.class);
        alertData.setAlertRuleId(alertRule.getId());
        alertData.setAlertReason(alertReason);
        alertData.setType(alertRule.getAlertDataType());
        alertData.setStatus(0);

        List<AlertData> list = consumerIds.stream().map(id -> {
            AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
            dbAlertData.setUserId(id);
            return dbAlertData;
        }).collect(Collectors.toList());
        alertDataMapper.batchInsert(list);
    }
}
